from random import randint
import logging
import re
from sqlite3 import DatabaseError
import json
from django.urls import reverse

import users
from home.models import ArticleCategory, Article
from lib.captcha.captcha import captcha
from lib.yuntongxun.sms import CCP
from users.models import User

logger = logging.getLogger('django')

from django.http import HttpResponseBadRequest, HttpResponse, JsonResponse, response
from django.shortcuts import render, redirect
from django.views import View
# Create your views here.


#CBV
#注册试图
from django_redis import get_redis_connection

# from libs.captcha.captcha.captcha import captcha
# from libs.yuntongxun.sms import CCP
from utils.response_code import RETCODE

from django.views import View
from django.contrib.auth import login
from django.contrib.auth import authenticate
from django.contrib.auth import logout

from django.contrib.auth.mixins import LoginRequiredMixin

#注册
class RegisterView(View):
    def get(self,request):
        return render(request,'register.html')
    def post(self,request):
        #接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode=request.POST.get('sms_code')

        # 判断参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必传参数')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号码')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9a-zA-Z]{8,20}$',password) or not re.match(r'^[0-9a-zA-Z]{8,20}$',password2):
            return HttpResponseBadRequest('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')

        #验证短信验证码
        redis_conn = get_redis_connection('default')
        sms_code_server = redis_conn.get('sms:%s' % mobile)
        print(redis_conn)
        if sms_code_server is None:
            return HttpResponseBadRequest('短信验证码已过期')
        if smscode != sms_code_server.decode():
            return HttpResponseBadRequest('短信验证码错误')

        # 保存注册数据
        try:
            user=User.objects.create_user(username=mobile,mobile=mobile, password=password)
            # 实现状态保持
            login(request, user)
            # 跳转到首页
            response = redirect(reverse('home:index'))
            # 设置cookie
            # 登录状态，会话结束后自动过期
            response.set_cookie('is_login', True)
            # 设置用户名有效期一个月
            response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
            return response
        except DatabaseError:
            return HttpResponseBadRequest('注册失败')

        # 响应注册结果
        return redirect(reverse('home:index'))

# 验证码视图
class ImageView(View):
    def get(self, request):
        '''
        步骤：
        1、接收前端传递过来的uuid
        2、判断uuid失分获取到
        3、通过调用captcha来生成图片验证码（返回：图片内容和图片二进制）
        4、将图片内容保存到redis中。uuid作为key，图片内容作为值，同时还需要设置一个过期时间
        5、返回图片给前端
        :param request:
        :return:
        '''

        # 1、接收前端传递过来的uuid
        uuid = request.GET.get('uuid')
        # 2、判断uuid失分获取到
        if uuid is None:
            return HttpResponseBadRequest('没有获取到uuid')
        # 3、通过调用captcha来生成图片验证码（返回：图片内容和图片二进制）
        txt, image = captcha.generate_captcha()


        # 4、将图片内容保存到redis中。uuid作为key，图片内容作为值，同时还需要设置一个过期时间
        redis_conn = get_redis_connection('default')
        # name:数据key，这里采用img前缀：uuid
        # time:300秒后过期
        # value：对应key的值
        redis_conn.setex(name='img:%s' % uuid, time=300, value=txt)
        # 5、返回图片给前端
        return HttpResponse(image, content_type='image/jpeg')

#短信验证
class SmsCodeView(View):

    def get(self,request):
        # 接收参数
        image_code = request.GET.get('image_code')
        uuid = request.GET.get('uuid')
        mobile=request.GET.get('mobile')

        # 校验参数
        if not all([image_code, uuid,mobile]):
            return JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数'})

        # 创建连接到redis的对象
        redis_conn = get_redis_connection('default')
        # 提取图形验证码
        image_code_server = redis_conn.get('img:%s' % uuid)
        if image_code_server is None:
            # 图形验证码过期或者不存在
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效'})
        # 删除图形验证码，避免恶意测试图形验证码
        try:
            redis_conn.delete('img:%s' % uuid)
        except Exception as e:
            logger.error(e)
        # 对比图形验证码
        image_code_server = image_code_server.decode()  # bytes转字符串
        if image_code.lower() != image_code_server.lower():  # 转小写后比较
            return JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})

        # 生成短信验证码：生成6位数验证码
        sms_code = '%06d' % randint(0, 999999)
        #将验证码输出在控制台，以方便调试
        logger.info(sms_code)
        # 保存短信验证码到redis中，并设置有效期
        redis_conn.setex('sms:%s' % mobile, 300, sms_code)
        # 发送短信验证码
        CCP().send_template_sms(mobile, [sms_code, 5],1)

        # 响应结果
        return JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})

#登录
class LoginView(View):
    def get(self,request):
        return render(request,'login.html')
    def post(self,request):
        '''
          实现思路：
          1、接收提交参数
          2、验证参数
           2-1、手机号码是否符合规则
           2-2、密码是否符合规则
          3、用户认证登录
          4、状态保持
          5、根据用户选择的是否记住登录状态进行判断
          6、设置cookie信息，为首页显示服务
          7、跳转到首页
          :param request:
          :return:
          '''

        #接受参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        remember = request.POST.get('remember')

        # 校验参数
        if not all([mobile, password]):
            return HttpResponseBadRequest('缺少必要参数')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号码')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password):
            return HttpResponseBadRequest('请输入8-20位的密码')

        #认证登录用户
        # 认证字段已经在User模型中的USERNAME_FIELD = 'mobile'修改
        user = authenticate(mobile=mobile,password=password)
        if user is None:
            return HttpResponseBadRequest('用户名或密码错误')

        # 实现状态保持
        login(request, user)

        # 响应登录结果
        response = redirect(reverse('home:index'))

        ## 响应登录结果
        response =  redirect(reverse('home:index'))

        # 设置状态保持的周期
        if remember != 'on':
            # 没有记住用户：浏览器会话结束就过期
            request.session.set_expiry(0)
            # 设置cookie
            response.set_cookie('is_login', True)

            u = json.dumps(user.username)
            response.set_cookie('username', u, max_age=30 * 24 * 3600)
        else:
            # 记住用户：None表示两周后过期
            request.session.set_expiry(None)
            # 设置cookie
            response.set_cookie('is_login', True, max_age=14*24 * 3600)
            response.set_cookie('username', user.username, max_age=30 * 24 * 3600)
        #返回响应
        return response

#退出登录
class LogoutView(View):

    def get(self,request):
        # 清理session
        logout(request)
        # 退出登录，重定向到登录页
        response = redirect(reverse('home:index'))
        # 退出登录时清除cookie中的登录状态
        response.delete_cookie('is_login')

        return response

#忘记密码
class ForgetPasswordView(View):
    def get(self, request):
        return render(request, 'forget_password.html')
    def post(self,request):
        ''' 实现思路：
        1、接收数据
        2、验证数据
            2-1、判断参数是否齐全
            2-2、手机号是否符合规则
            2-3、判断密码是否符合规则
            2-4、判断确认密码是否一致
            2-5、判断短信验证码是否正确
        3、根据手机号码进行用户信息的查询
        4、如果手机号查询出用户信息则进行用户密码的修改
        5、如果手机号没有查询出用户信息，则进行新用户的创建
        6、进行页面跳转，跳转到登录页面
        7、返回响应 :param request: :return: '''
        # 接收参数
        mobile = request.POST.get('mobile')
        password = request.POST.get('password')
        password2 = request.POST.get('password2')
        smscode = request.POST.get('sms_code')

        # 判断参数是否齐全
        if not all([mobile, password, password2, smscode]):
            return HttpResponseBadRequest('缺少必传参数')
        # 判断手机号是否合法
        if not re.match(r'^1[3-9]\d{9}$', mobile):
            return HttpResponseBadRequest('请输入正确的手机号码')
        # 判断密码是否是8-20个数字
        if not re.match(r'^[0-9a-zA-Z]{8,20}$', password) or not re.match(r'^[0-9a-zA-Z]{8,20}$', password2):
            return HttpResponseBadRequest('请输入8-20位的密码')
        # 判断两次密码是否一致
        if password != password2:
            return HttpResponseBadRequest('两次输入的密码不一致')

        # 验证短信验证码
        redis_conn = get_redis_connection('default')
        redis_sms_code = redis_conn.get('sms:%s' % mobile)
        if redis_sms_code is None:
            return HttpResponseBadRequest('短信验证码过期')
        try:
            if redis_sms_code.decode() != smscode:
                return HttpResponseBadRequest('验证码错误')
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('验证码错误')

        # 3、根据手机号码进行用户信息的查询
        return_user = User.objects.filter(mobile=mobile).first()
        # 4、如果手机号查询出用户信息则进行用户密码的修改
        # 5、如果手机号没有查询出用户信息，则进行新用户的创建
        if return_user is None:
            try:
                User.objects.create_user(username=mobile,mobile=mobile,password=password)
            except Exception as e:
                logger.error(e)
                return HttpResponseBadRequest('修改密码失败')
        else:
            try:
                # 调用系统user对象的set_password()进行修改密码，该方法会对密码进行加密
                return_user.set_password(password)
                return_user.save()
            except Exception as e:
                logger.error(e)
                return HttpResponseBadRequest('修改密码失败')
        # 6、进行页面跳转，跳转到登录页面
        resp = redirect(reverse('users:login'))

        # 7、返回响应
        return resp

#用户中心
class UserCenterView(LoginRequiredMixin,View):

    def get(self,request):
        # request.user.is_authenticated()
        # 来判断用户是否登录。如果通过登录验证则返回True。反之，返回False。
        # LoginRequiredMixin封装了判断用户是否登录的操作。
        user = request.user
        context = {
            'username' : user.username,
            'mobile':user.mobile,
            'avatar': user.avatar.url if user.avatar else None,
            'user_desc': user.user_desc
        }
        return render(request,'usercenter.html',context=context)

    def post(self,request):
        user = request.user
        username = request.POST.get('username')
        user_desc = request.POST.get('desc')
        avatar = request.FILES.get('avatar')
        # 修改数据库数据
        try:
            user.username = username
            user.user_desc = user_desc
            if avatar:
                user.avatar = avatar
            user.save()
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('更新失败，请稍后再试')

        # 返回响应，刷新页面
        response = redirect(reverse('users:usercenter'))
        response.set_cookie('login_name',username)
        return response

#写文章
class WriteBlogView(LoginRequiredMixin,View):
    def get(self,request):
        # 获取博客分类信息
        categories = ArticleCategory.objects.all()

        context = {
            'categories': categories
        }
        return render(request,'write_blog.html',context=context)

    def post(self,request):
        #接收数据
        avatar = request.FILES.get('avatar')
        title = request.POST.get('title')
        category_id = request.POST.get('category')
        tags = request.POST.get('tags')
        sumary = request.POST.get('sumary')
        content = request.POST.get('content')
        user = request.user

        # 验证数据是否齐全
        if not all([avatar, title, category_id, sumary, content]):
            return HttpResponseBadRequest('参数不全')

        # 判断文章分类id数据是否正确
        try:
            article_category = ArticleCategory.objects.get(id=category_id)
        except ArticleCategory.DoesNotExist:
            return HttpResponseBadRequest('没有此分类信息')

        # 保存到数据库
        try:
            article = Article.objects.create(
                author=user,
                avatar=avatar,
                category=article_category,
                tags=tags,
                title=title,
                sumary=sumary,
                content=content
            )
            #article.save()
        except Exception as e:
            logger.error(e)
            return HttpResponseBadRequest('发布失败，请稍后再试')

        # 返回响应，跳转到文章详情页面
        # 暂时先跳转到首页
        return redirect(reverse('home:index'))

